/*!
 * \file      uart-board.h
 *
 * \brief     Target board UART driver implementation
 *
 * \copyright Revised BSD License, see section \ref LICENSE.
 *
 * \code
 *                ______                              _
 *               / _____)             _              | |
 *              ( (____  _____ ____ _| |_ _____  ____| |__
 *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
 *               _____) ) ____| | | || |_| ____( (___| | | |
 *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
 *              (C)2013-2017 Semtech
 *
 * \endcode
 *
 * \author    Miguel Luis ( Semtech )
 *
 * \author    Gregory Cristian ( Semtech )
 */
#include <stdint.h>
#include <stddef.h>
#include "uart.h"
#include "n32l43x_usart.h"
#include "utilities.h"

struct UartHandleType{
    USART_Module* Instance;
    USART_InitType Init;
    Uart_t *uart;
};

#define TX_BUFFER_RETRY_COUNT       10

static struct UartHandleType UartHandle[2]={0};

/*!
 * \brief Initializes the UART object and MCU peripheral
 *
 * \param [IN] obj  UART object
 * \param [IN] tx   UART Tx pin name to be used
 * \param [IN] rx   UART Rx pin name to be used
 */
void UartMcuInit( Uart_t *obj, UartId_t uartId, PinNames tx, PinNames rx )
{
    obj->UartId = uartId;
    if(uartId == UART_1) {
        RCC_EnableAPB2PeriphReset(RCC_APB2_PERIPH_USART1, ENABLE);
        RCC_EnableAPB2PeriphReset(RCC_APB2_PERIPH_USART1, DISABLE);
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_USART1, ENABLE);
        
        UartHandle[uartId].Instance = USART1;
        
        GpioInit(&obj->Tx, tx, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, GPIO_AF4_USART1);
        GpioInit(&obj->Rx, rx, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, GPIO_AF4_USART1);
    } else if(uartId == UART_2) {
        RCC_EnableAPB1PeriphReset(RCC_APB1_PERIPH_USART2, ENABLE);
        RCC_EnableAPB1PeriphReset(RCC_APB1_PERIPH_USART2, DISABLE);
        RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_USART2, ENABLE);
        
        UartHandle[uartId].Instance = USART2;
        
        GpioInit(&obj->Tx, tx, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, GPIO_AF4_USART2);
        GpioInit(&obj->Rx, rx, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, GPIO_AF4_USART2);
    }
    UartHandle[uartId].uart = obj;
    
}

/*!
 * \brief Configures the UART object and MCU peripheral
 *
 * \remark UartInit function must be called first.
 *
 * \param [IN] obj          UART object
 * \param [IN] mode         Mode of operation for the UART
 * \param [IN] baudrate     UART baudrate
 * \param [IN] wordLength   packet length
 * \param [IN] stopBits     stop bits setup
 * \param [IN] parity       packet parity
 * \param [IN] flowCtrl     UART flow control
 */
void UartMcuConfig( Uart_t *obj, UartMode_t mode, uint32_t baudrate, WordLength_t wordLength, StopBits_t stopBits, Parity_t parity, FlowCtrl_t flowCtrl )
{
    NVIC_InitType NVIC_InitStructure;
    struct UartHandleType *uart = NULL;
    if(obj->UartId == UART_1) {
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        uart = &UartHandle[obj->UartId];
    } else if(obj->UartId == UART_2) {
        NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
        uart = &UartHandle[obj->UartId];
    } else {
        return ;
    }
    USART_InitType *Init = &uart->Init;
    Init->BaudRate = baudrate;
    if(mode == TX_ONLY) {
        if(obj->FifoTx.Data == NULL) {
            assert_param(LMN_STATUS_ERROR);
        }
        Init->Mode = USART_MODE_TX;
    } else if(mode == RX_ONLY) {
        if(obj->FifoRx.Data == NULL) {
            assert_param(LMN_STATUS_ERROR);
        }
        Init->Mode = USART_MODE_RX;
    } else if(mode == RX_TX) {
        if((obj->FifoRx.Data == NULL)||(obj->FifoTx.Data == NULL)) {
            assert_param(LMN_STATUS_ERROR);
        }
        Init->Mode = USART_MODE_RX|USART_MODE_TX;
    } else {
        assert_param(LMN_STATUS_ERROR);
    }
    if(wordLength == UART_8_BIT) {
        Init->WordLength = USART_WL_8B;
    } else if(wordLength == UART_9_BIT) {
        Init->WordLength = USART_WL_9B;
    }
    switch(stopBits) {
    case UART_1_5_STOP_BIT:
        Init->StopBits = USART_STPB_1_5;
        break;
    case UART_2_STOP_BIT:
        Init->StopBits = USART_STPB_2;
        break;
    case UART_1_STOP_BIT:
    default:
        Init->StopBits = USART_STPB_1;
        break;
    }
    switch(parity) {
    case NO_PARITY:
        Init->Parity = USART_PE_NO;
        break;
    case ODD_PARITY:
        Init->Parity = USART_PE_ODD;
        break;
    case EVEN_PARITY:
    default:
        Init->Parity = USART_PE_EVEN;
        break;
    }
    switch(flowCtrl) {
    case NO_FLOW_CTRL:
        Init->HardwareFlowControl = USART_HFCTRL_NONE;
        break;
    case RTS_FLOW_CTRL:
        Init->HardwareFlowControl = USART_HFCTRL_RTS;
        break;
    case CTS_FLOW_CTRL:
        Init->HardwareFlowControl = USART_HFCTRL_CTS;
        break;
    case RTS_CTS_FLOW_CTRL:
        Init->HardwareFlowControl = USART_HFCTRL_RTS_CTS;
        break;
    }
    USART_Init(uart->Instance, Init);
    USART_ConfigInt(uart->Instance, USART_INT_RXDNE, ENABLE);
    USART_ConfigInt(uart->Instance, USART_INT_TXC, ENABLE);
    USART_Enable(uart->Instance, ENABLE);
    
    /* Enable the USARTy Interrupt */
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/*!
 * \brief DeInitializes the UART object and MCU pins
 *
 * \param [IN] obj  UART object
 */
void UartMcuDeInit( Uart_t *obj )
{
    if(obj->UartId == UART_1) {
        RCC_EnableAPB2PeriphReset(RCC_APB2_PERIPH_USART1, ENABLE);
        RCC_EnableAPB2PeriphReset(RCC_APB2_PERIPH_USART1, DISABLE);
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_USART1, ENABLE);
    } else if(obj->UartId == UART_2) {
        RCC_EnableAPB1PeriphReset(RCC_APB1_PERIPH_USART2, ENABLE);
        RCC_EnableAPB1PeriphReset(RCC_APB1_PERIPH_USART2, DISABLE);
        RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_USART2, ENABLE);
    } else {
        return ;
    }
    GpioInit(&obj->Tx, obj->Tx.pin, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 0);
    GpioInit(&obj->Rx, obj->Rx.pin, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 0);
}

/*!
 * \brief Sends a character to the UART
 *
 * \param [IN] obj   UART object
 * \param [IN] data  Character to be sent
 * \retval status    [0: OK, 1: Busy]
 */
uint8_t UartMcuPutChar( Uart_t *obj, uint8_t data )
{
    CRITICAL_SECTION_BEGIN( );

    if( IsFifoFull( &obj->FifoTx ) == false )
    {
        FifoPush( &obj->FifoTx, data );

        // Trig UART Tx interrupt to start sending the FIFO contents.
        if(USART_GetFlagStatus(UartHandle[obj->UartId].Instance, USART_FLAG_TXDE) == SET) {
            USART_SendData(UartHandle[obj->UartId].Instance, FifoPop(&obj->FifoTx));
        }

        CRITICAL_SECTION_END( );
        return 0; // OK
    }
    CRITICAL_SECTION_END( );
    return 1; // Busy
}

/*!
 * \brief Sends a buffer to the UART
 *
 * \param [IN] obj    UART object
 * \param [IN] buffer Buffer to be sent
 * \param [IN] size   Buffer size
 * \retval status     [0: OK, 1: Busy]
 */
uint8_t UartMcuPutBuffer( Uart_t *obj, uint8_t *buffer, uint16_t size )
{
    uint8_t retryCount;
    uint16_t i;

    for( i = 0; i < size; i++ )
    {
        retryCount = 0;
        while( UartPutChar( obj, buffer[i] ) != 0 )
        {
            retryCount++;

            // Exit if something goes terribly wrong
            if( retryCount > TX_BUFFER_RETRY_COUNT )
            {
                return 1; // Error
            }
        }
    }
    return 0; // OK
}

/*!
 * \brief Gets a character from the UART
 *
 * \param [IN] obj   UART object
 * \param [IN] data  Received character
 * \retval status    [0: OK, 1: Busy]
 */
uint8_t UartMcuGetChar( Uart_t *obj, uint8_t *data )
{
    CRITICAL_SECTION_BEGIN( );

    if( IsFifoEmpty( &obj->FifoRx ) == false )
    {
        *data = FifoPop( &obj->FifoRx );
        CRITICAL_SECTION_END( );
        return 0;
    }
    CRITICAL_SECTION_END( );
    return 1;
}

/*!
 * \brief Gets a character from the UART
 *
 * \param [IN] obj          UART object
 * \param [IN] buffer       Received buffer
 * \param [IN] size         Number of bytes to be received
 * \param [OUT] nbReadBytes Number of bytes really read
 * \retval status           [0: OK, 1: Busy]
 */
uint8_t UartMcuGetBuffer( Uart_t *obj, uint8_t *buffer, uint16_t size, uint16_t *nbReadBytes )
{
    uint16_t localSize = 0;

    while( localSize < size )
    {
        if( UartGetChar( obj, buffer + localSize ) == 0 )
        {
            localSize++;
        }
        else
        {
            break;
        }
    }

    *nbReadBytes = localSize;

    if( localSize == 0 )
    {
        return 1; // Empty
    }
    return 0; // OK
}

void UART_IRQHandler(struct UartHandleType *uartHandle)
{
    USART_Module *instance = uartHandle->Instance;
    Uart_t *uart = uartHandle->uart;
    if(USART_GetIntStatus(instance, USART_INT_RXDNE) != RESET) {
        if(IsFifoFull(&uart->FifoRx) == false) {
            FifoPush(&uart->FifoRx, (uint8_t)USART_ReceiveData(instance));
        }
        if(uart->IrqNotify != NULL) {
            uart->IrqNotify(UART_NOTIFY_RX);
        }
        USART_ClrIntPendingBit(instance, USART_INT_RXDNE);
    }
    if(USART_GetIntStatus(instance, USART_INT_TXC) != RESET) {
        if(IsFifoEmpty(&uart->FifoTx) == false) {
            uint8_t data = FifoPop(&uart->FifoTx);
            USART_SendData(instance, data);
        }
        if(uart->IrqNotify != NULL) {
            uart->IrqNotify(UART_NOTIFY_TX);
        }
        USART_ClrIntPendingBit(instance, USART_INT_TXC);
    }
}

void USART1_IRQHandler( void )
{
    UART_IRQHandler( &UartHandle[UART_1] );
}

void USART2_IRQHandler( void )
{
    UART_IRQHandler( &UartHandle[UART_2] );
}


